#ifndef DYNARRAY_H_
#define DYNARRAY_H_
#include <cstdlib>

template<typename Type>
class DynArray
{
private:
	Type *Array;
	unsigned int Size;
	unsigned int Capacity;
	const static int multiplier = 2;

public:
	/////////////////////////////////////////////////////////////////////////////
	// Function :	Constructor
	// Notes : Constructs an empty array (Size 0 Capacity 0)
	/////////////////////////////////////////////////////////////////////////////
	DynArray();

	/////////////////////////////////////////////////////////////////////////////
	// Function :	Destructor
	// Notes : cleans up any dynamic memory
	/////////////////////////////////////////////////////////////////////////////
	~DynArray();

	/////////////////////////////////////////////////////////////////////////////
	// Function :	Copy Constructor
	/////////////////////////////////////////////////////////////////////////////
	DynArray(const DynArray<Type>& that);

	/////////////////////////////////////////////////////////////////////////////
	// Function :	Assignment Operator
	/////////////////////////////////////////////////////////////////////////////
	DynArray<Type>& operator=(const DynArray<Type>& that);

	/////////////////////////////////////////////////////////////////////////////
	// Function : operator[]
	// Parameters : index - the index to access
	// Return : Type & - returns the array element at [index]
	// Notes : performs no error checking. user should ensure index is 
	//		valid with the size() method
	/////////////////////////////////////////////////////////////////////////////
	Type& operator[](const unsigned int& index);

	/////////////////////////////////////////////////////////////////////////////
	// Function :	size
	// Returns : int - returns the number of items being stored
	// Notes : this function returns the number of items being stored, 
	/////////////////////////////////////////////////////////////////////////////
	unsigned int size() const;



	/////////////////////////////////////////////////////////////////////////////
	// Function : capacity
	// Returns : int - returns the number of items the array can store before 
	//		the next resize
	// Notes : this function returns the number of items the array can store, 
	//		not the number of bytes
	/////////////////////////////////////////////////////////////////////////////
	unsigned int capacity() const;

	/////////////////////////////////////////////////////////////////////////////
	// Function :	clear
	// Notes : cleans up any dynamic memory and resets size and capacity to 0
	/////////////////////////////////////////////////////////////////////////////
	void clear();

	/////////////////////////////////////////////////////////////////////////////
	// Function : append
	// Parameters : item - the item to be appended to the next open spot
	// Notes : this function will append the item to the next open spot. if 
	//		no room exists, the array's capacity will be doubled and then 
	//		the item will be added
	/////////////////////////////////////////////////////////////////////////////
	void append(const Type& item);

	/////////////////////////////////////////////////////////////////////////////
	// Function : reserve
	// Parameters : newCap - the new capacity
	// Notes : 	- default parameter - reserve more space in the array, based on //		the expansion rate (100%, 1 minimum).
	//		- non-default parameter, expand to the specified capacity
	//		- if newCap is LESS than the current capacity, do nothing. 
	//		This function should NOT make the array smaller.
	/////////////////////////////////////////////////////////////////////////////
	void reserve(const unsigned int & newCap = 0);


};

/////////////////////////////////////////////////////////////////////////////
// Function :	Constructor
// Notes : Constructs an empty array (Size 0 Capacity 0)
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
DynArray<Type>::DynArray() : Size(0), Capacity(0)
{
	Array = nullptr;
}

/////////////////////////////////////////////////////////////////////////////
// Function :	Destructor
// Notes : cleans up any dynamic memory
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
DynArray<Type>::~DynArray()
{
	delete[] Array;
}

/////////////////////////////////////////////////////////////////////////////
// Function :	Copy Constructor
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
DynArray<Type>::DynArray(const DynArray<Type>& that) : Size(that.Size), Capacity(that.Capacity)
{
	Array = nullptr;
	delete[] Array;
	Array = new Type[Capacity];
	for(unsigned int i = 0; i < Size; i++)
		Array[i] = that.Array[i];
}

/////////////////////////////////////////////////////////////////////////////
// Function :	Assignment Operator
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
DynArray<Type>& DynArray<Type>::operator=(const DynArray<Type>& that)
{

	if(&that != this)
	{
		Size = that.Size;
		Capacity = that.Capacity;
		delete[] Array;
		Array = new Type[Capacity];
		for(unsigned int i = 0; i < Size; i++)
			Array[i] = that.Array[i];
	}

	return *this;

}

/////////////////////////////////////////////////////////////////////////////
// Function : operator[]
// Parameters : index - the index to access
// Return : Type & - returns the array element at [index]
// Notes : performs no error checking. user should ensure index is 
//		valid with the size() method
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
Type& DynArray<Type>::operator[](const unsigned int& index)
{
	return Array[index];

}

/////////////////////////////////////////////////////////////////////////////
// Function :	size
// Returns : int - returns the number of items being stored
// Notes : this function returns the number of items being stored, 
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
unsigned int DynArray<Type>::size() const
{
	return Size;
}

/////////////////////////////////////////////////////////////////////////////
// Function : capacity
// Returns : int - returns the number of items the array can store before 
//		the next resize
// Notes : this function returns the number of items the array can store, 
//		not the number of bytes
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
unsigned int DynArray<Type>::capacity() const
{
	return Capacity;
}

/////////////////////////////////////////////////////////////////////////////
// Function :	clear
// Notes : cleans up any dynamic memory and resets size and capacity to 0
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void DynArray<Type>::clear()
{
	delete[] Array;
	Array = nullptr;
	Size = 0;
	Capacity = 0;
}

/////////////////////////////////////////////////////////////////////////////
// Function : append
// Parameters : item - the item to be appended to the next open spot
// Notes : this function will append the item to the next open spot. if 
//		no room exists, the array's capacity will be doubled and then 
//		the item will be added
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void DynArray<Type>::append(const Type& item)
{

	if(0 == Capacity)
	{
		Array = new Type[1];
		Array[Size] = item;
		Size = 1;
		Capacity = 1;
	}

	else if(Capacity == Size)
	{
		Capacity *= multiplier;
		Type* temporary = new Type[Capacity];
		for(unsigned int i = 0; i < Size; i++)
			temporary[i] = Array[i];
		delete[] Array;
		Array = temporary;
		Array[Size] = item;
		Size = Size + 1;
	}

	else
	{
		Array[Size] = item;
		Size = Size + 1;
	}

}

/////////////////////////////////////////////////////////////////////////////
// Function : reserve
// Parameters : newCap - the new capacity
// Notes : 	- default parameter - reserve more space in the array, based on //		the expansion rate (100%, 1 minimum).
//		- non-default parameter, expand to the specified capacity
//		- if newCap is LESS than the current capacity, do nothing. 
//		This function should NOT make the array smaller.
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void DynArray<Type>::reserve(const unsigned int & newCap = 0)
{

	if(newCap == 0)
	{
		if(Capacity == 0)
		{
			Array = new Type[1];
			Array[Size] = newCap;
			Capacity = 1;
		}
		else
		{
			Capacity *= multiplier;
			Type* Temporary = new Type[Capacity];
			for(unsigned int i = 0; i < Size; i++)
				Temporary[i] = Array[i];

			delete[] Array;
			Array = Temporary;
		}
	}
	else if(Capacity > newCap)
	{

	}

	else if(newCap >= 1)
	{
		Capacity = newCap;
		Type* Temporary = new Type[Capacity];
		for(unsigned int i = 0; i < Size; i++)
			Temporary[i] = Array[i];

		delete[] Array;
		Array = Temporary;
	}

}

#endif